package animation;

import com.crunch.graphics.gpu.GpuDrawCall;
import com.crunch.graphics.gpu.GpuProgram;
import com.crunch.graphics.gpu.GpuState;
import com.crunch.graphics.gpu.GpuTexture;
import com.crunch.math.Matrix44f;
import com.crunch.math.Vector2f;
import com.crunch.math.Vector4f;

import javax.media.opengl.GL;
import javax.media.opengl.GL3;

/**
 * Renders skin.
 */
public class SkinRenderer {
	public SkinRenderer(GpuProgram program) {
		this.program = program;

		mvpMatrixIndex = program.getUniformIndex("mvpMatrix");
		xyxyIndex = program.getUniformIndex("xyxy");
		txyxyIndex = program.getUniformIndex("txyxy");
		texIndex = program.getUniformIndex("tex");
		colorIndex = program.getUniformIndex("color");
	}

	public void drawSkin(GpuState gpuState, Matrix44f viewProjectionMatrix, Character.State state, SkinInstance skin, GpuTexture texture, Vector4f color) {
		if (skin.getSkinComponentCollection().getScale() == 0.0f) {
			return;
		}

		// attribute-less rendering
		gpuState.unbindBuffer(GL.GL_ARRAY_BUFFER);
		program.bind();

		program.setUniform(colorIndex, GL3.GL_FLOAT_VEC4, color.getArray());
		program.setUniform(texIndex, GL3.GL_SAMPLER_2D, new int[] { 0 });
		texture.bind(0);

		float invWidth = 1.0f / (float) texture.getWidth();
		float invHeight = 1.0f / (float) texture.getHeight();
		float invScale = 1.0f / skin.getSkinComponentCollection().getScale();

		float[] xyxy = new float[4];
		float[] txyxy = new float[4];

		for (int i = 0; i < state.getCharacter().getSkeleton().getBoneCount(); ++i) {
			int index = state.getCharacter().getSkeleton().getDrawingOrderedBone(i);
			int compIndex = skin.getSkinComponentIndex(index);
			if (compIndex < 0) {
				continue;
			}
			SkinComponentCollection.Component component = skin.getSkinComponentCollection().getComponent(compIndex);

			Matrix44f mvpMatrix = viewProjectionMatrix.multiply(state.getBoneMatrices()[index].getGraphicsMatrix());

			Vector2f origin = component.getOrigin();
			Vector2f boundMin = component.getBoundMin();
			Vector2f boundMax = component.getBoundMax();

			// since skin coords are relative to TOP left, we negate y
			// convert from pixels to units by dividing by scale
			xyxy[0] = (boundMin.x() - origin.x()) * invScale;
			xyxy[1] = (origin.y() - boundMax.y()) * invScale;
			xyxy[2] = (boundMax.x() - origin.x()) * invScale;
			xyxy[3] = (origin.y() - boundMin.y()) * invScale;

			// flip y tex coord
			txyxy[0] = boundMin.x() * invWidth;
			txyxy[1] = boundMax.y() * invHeight;
			txyxy[2] = boundMax.x() * invWidth;
			txyxy[3] = boundMin.y() * invHeight;

			program.setUniform(mvpMatrixIndex, GL3.GL_FLOAT_MAT4, mvpMatrix.getArray());
			program.setUniform(xyxyIndex, GL3.GL_FLOAT_VEC4, xyxy);
			program.setUniform(txyxyIndex, GL3.GL_FLOAT_VEC4, txyxy);

			GpuDrawCall.DrawCall dc = gpuState.createDrawCall();
			dc.setParameters(GL.GL_POINTS, 0, 1);
			dc.draw();
		}
	}

	public GpuProgram program;
	private int mvpMatrixIndex;
	private int xyxyIndex;
	private int txyxyIndex;
	private int texIndex;
	private int colorIndex;
}
